package tps12.axwax.client;

import java.util.ArrayList;

import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.SpanElement;
import com.google.gwt.dom.client.Node;
import com.google.gwt.dom.client.Text;

public class SpanMeasurement {
	public static int countTextCharacters(Element element) {
		return element.getInnerText().length();
	}
	
	public static int trimTextCharacters(Element element, int length) {
		int i = 0; // the number of text characters that have been seen
		Node child = null;
		for (child = element.getFirstChild(); child != null; child = child.getNextSibling()) {
			if (child.getNodeType() == Node.ELEMENT_NODE)
				i += trimTextCharacters((Element)child, length - i);
			else {
				Text text = (Text)child;
				if (text.getLength() < length - i)
					i += text.getLength();
				else {
					text.setData(text.getData().substring(0, length - i));
					i = length;
				}
			}
			if (i == length)
				break;
		}
		if (child != null) {
			child = child.getNextSibling();
			while (child != null) {
				Node next = child.getNextSibling();
				child.removeFromParent();
				child = next;
			}
		}
		return i;
	}
	
	public static boolean isFirstTextCharacterSpace(Element element) {
		Node child = element.getFirstChild();
		if (child.getNodeType() == Node.ELEMENT_NODE)
			return isFirstTextCharacterSpace((Element)child);
		else {
			Text text = (Text)child;
			String s = text.getData();
			if (s.length() > 0) {
				char c = s.charAt(0);
				return c == ' ' || c == '\t' || c == '\r' || c == '\n';
			}
			else
				return false;
		}
	}
	
	public static boolean trimLastTextCharacterWasSpace(Element element) {
		String html = element.getInnerHTML();
		trimTextCharactersEnd(element, 1);
		boolean res = isFirstTextCharacterSpace(element);
		element.setInnerHTML(html);
		trimTextCharacters(element, countTextCharacters(element) - 1);
		return res;
	}

	public static int trimTextCharactersEnd(Element element, int length) {
		int i = 0; // the number of text characters that have been seen
		Node child = null;
		for (child = element.getLastChild(); child != null; child = child.getPreviousSibling()) {
			if (child.getNodeType() == Node.ELEMENT_NODE)
				i += trimTextCharactersEnd((Element)child, length - i);
			else {
				Text text = (Text)child;
				if (text.getLength() < length - i)
					i += text.getLength();
				else {
					text.setData(text.getData().substring(text.getLength() - (length - i), text.getLength()));
					i = length;
				}
			}
			if (i == length)
				break;
		}
		if (child != null) {
			child = child.getPreviousSibling();
			while (child != null) {
				Node previous = child.getPreviousSibling();
				child.removeFromParent();
				child = previous;
			}
		}
		return i;
	}

	public static void trimTextCharacters(Element element, int start, int length) {
		trimTextCharacters(element, start + length);
		trimTextCharactersEnd(element, length);
	}
	
	public static int getTextCharacterIndexFromLocation(String html, Element ruler, int x) {	
		ruler.setInnerHTML(html);
		for (int i = countTextCharacters(ruler)-1; i >= 0; i--) {
			SpanMeasurement.trimTextCharacters(ruler, i);
			if (ruler.getOffsetWidth() < x)
				return i+1;
		}
		return 0;
	}
	
	public static int[] getTextRowFromLocation(String html, Element ruler, int y) {
		ruler.setInnerHTML(html);
		int h = ruler.getOffsetHeight();
		int[] row = {-1,countTextCharacters(ruler)};
		for (int i = countTextCharacters(ruler)-1; i >= 0; i--) {
			boolean space = trimLastTextCharacterWasSpace(ruler); 
				//SpanMeasurement.trimTextCharacters(ruler, i);
			if (ruler.getOffsetHeight() != h) {
				// removing last character caused row change
				h = ruler.getOffsetHeight();
				if (h < y) {
					// was in target row
					// need to keep going until space reached
					while (!space) {
						space = trimLastTextCharacterWasSpace(ruler);
						row[1]--;
					}
					return row;
				}
				else {
					// wasn't target row, clear row indices
					row[0] = -1;
					row[1] = i-1;
				}
			}
			else
				row[0] = i-1;
		}
		return row;
	}
	
	public static String getFirstTextRow(String html, Element ruler) {
		ruler.setInnerHTML(html);
		int length = countTextCharacters(ruler);
		trimTextCharacters(ruler, 1);
		int h = ruler.getOffsetHeight();
		for (int i = 2; i < length; i++) {
			ruler.setInnerHTML(html);
			trimTextCharacters(ruler, i);
			if (ruler.getOffsetHeight() != h) {
				int w = ruler.getOffsetWidth();
				for (int j = i-1; j > 0; j--) {
					ruler.setInnerHTML(html);
					trimTextCharacters(ruler, j);
					if (ruler.getOffsetWidth() == w) {
						return ruler.getInnerHTML();
					}
				}
			}
		}		
		return html;
	}
	
	public static String[] getTextRows(String html, Element ruler) {
		String row = getFirstTextRow(html, ruler);
		if (row.equals(html)) {
			String[] res = new String[1];
			res[0] = row;
			return res;
		}
		ruler.setInnerHTML(row);
		int rowlength = countTextCharacters(ruler);
		ruler.setInnerHTML(html);
		trimTextCharactersEnd(ruler, countTextCharacters(ruler) - rowlength);
		String[] rest = getTextRows(ruler.getInnerHTML(), ruler);
		String[] rows = new String[rest.length + 1];
		rows[0] = row;
		for (int i = 0; i < rest.length; i++)
			rows[i+1] = rest[i];
		return rows;
	}
	
	public static int getFirstTextRowHeightDifference(String html, Element ruler) {
		ruler.setInnerHTML(html);
		int length = countTextCharacters(ruler);
		trimTextCharacters(ruler, 1);
		int h = ruler.getOffsetHeight();
		for (int i = 2; i < length; i++) {
			ruler.setInnerHTML(html);
			trimTextCharacters(ruler, i);
			if (ruler.getOffsetHeight() != h)
				return ruler.getOffsetHeight() - h;
		}		
		return 0;
	}

	public static int[] getTextRowHeightDifferences(String html, Element ruler) {
		int dh = getFirstTextRowHeightDifference(html, ruler);
		if (dh == 0)
			return new int[0];
		String row = getFirstTextRow(html, ruler);
		ruler.setInnerHTML(row);
		int rowlength = countTextCharacters(ruler);
		ruler.setInnerHTML(html);
		trimTextCharactersEnd(ruler, countTextCharacters(ruler) - rowlength);
		int[] rest = getTextRowHeightDifferences(ruler.getInnerHTML(), ruler);
		int[] result = new int[rest.length + 1];
		result[0] = dh;
		for (int i = 0; i < rest.length; i++)
			result[i+1] = rest[i];
		return result;
	}

	public static int[] getTextRowLocations(String html, Element ruler) {
		int[] dhs = getTextRowHeightDifferences(html, ruler);
		int[] result = new int[dhs.length+1];
		result[0] = 0;
		for (int i = 0; i < dhs.length; i++)
			result[i+1] = result[i] + dhs[i];
		return result;
	}

	public static int getTextCharacterIndexFromLocation(String html, Element ruler, int x, int y) {	
		int[] rowLocs = getTextRowLocations(html, ruler);
		for (int i = 0; i < rowLocs.length; i++)
			if (i == rowLocs.length-1 || y < rowLocs[i+1]) {
				String[] rows = getTextRows(html, ruler);
				int index = 0;
				for (int j = 0; j < i; j++) {
					ruler.setInnerHTML(rows[j]);
					index += countTextCharacters(ruler);
				}
				return index + getTextCharacterIndexFromLocation(rows[i], ruler, x);
			}
		return 0;
	}
}
